//
// Created by LONG on 2020/10/19.
//

#include "../include/l2Api.h"
#include "../../lcore/misc/misc.h"
#include "l1Intf/include/l1IntfApi.h"
#include "l3Intf/include/l3IntfApi.h"

S_UeL2Context *gpUeL2Context = NULL;

#define UE_L2_POLL_TIMEOUT  10

int32_t UE_L2_LoadUeInstConfig(S_UeL2UeInst *pUeInst, S_UeInstContext *pUeInstContext)
{
    pUeInst->inUse = pUeInstContext->inUse;

    pUeInst->slotNum = 20;

    pUeInst->pL2L1CpMsgQue = pUeInstContext->pL2L1CpMsgQue;
    pUeInst->pL1L2CpMsgQue = pUeInstContext->pL1L2CpMsgQue;
    pUeInst->pL2L1UpMsgQue = pUeInstContext->pL2L1UpMsgQue;
    pUeInst->pL1L2UpMsgQue = pUeInstContext->pL1L2UpMsgQue;

    return O5G_RC_OK;
}

int32_t UE_L2_LoadConfig()
{
    int32_t rc;
    uint32_t loop;

    gpUeL2Context->ueInstNum = gpUeContext->ueInstNum;
    for (loop = 0; loop < gpUeContext->ueInstNum; loop++)
    {
        rc = UE_L2_LoadUeInstConfig(&gpUeL2Context->ueInst[loop], &gpUeContext->ueInstContext[loop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_LoadConfig() UE_L2_LoadUeInstConfig() error!\n");
    }

    return O5G_RC_OK;
}

int32_t UE_L2_Init(void)
{
    int32_t rc;
    uint32_t ueLoop;
    S_UeL2UeInst *pUeInst;
    char ttiIntPipeName[256];

    gpUeL2Context = OS_MemCalloc(1, sizeof(S_UeL2Context));
    IF_RETURN(==, NULL, gpUeL2Context, O5G_RC_ER, "UE_L2_Init() OS_MemCalloc() error!\n");

    rc = UE_L2_LoadConfig();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Init() UE_L2_LoadConfig() error!\n");

    UE_INIT_INST_IDX(gpUeL2Context);

    rc = OS_SelectInit(&gpUeL2Context->ulTtiSelector, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Init() OS_SelectInit(ulTtiSelector) error!\n");
    rc = OS_SelectInit(&gpUeL2Context->dlTtiSelector, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Init() OS_SelectInit(dlTtiSelector) error!\n");

    gpUeL2Context->ulPoller = POLLER_Create(POLL_FD_NUM_MAX);
    IF_RETURN(==, NULL, gpUeL2Context->ulPoller, O5G_RC_ER, "UE_L2_Init() POLLER_Create(ulPoller) error!\n");
    gpUeL2Context->dlPoller = POLLER_Create(POLL_FD_NUM_MAX);
    IF_RETURN(==, NULL, gpUeL2Context->dlPoller, O5G_RC_ER, "UE_L2_Init() POLLER_Create(dlPoller) error!\n");

    gpUeL2Context->pL3iL1iMsgQue = VarRB_Create(NULL, 2 * 1024 * 1024, VAR_RB_TYPE_MPSC);
    gpUeL2Context->pL1iL3iMsgQue = VarRB_Create(NULL, 2 * 1024 * 1024, VAR_RB_TYPE_MPSC);

    for (ueLoop = 0; ueLoop < UE_UE_INST_NUM_MAX; ueLoop++)
    {
        pUeInst = &gpUeL2Context->ueInst[ueLoop];
        IF_CONTINUE(==, FALSE, pUeInst->inUse, NO_LOG_MESSAGE);

        snprintf(ttiIntPipeName, sizeof(ttiIntPipeName), "%s_ue%u", UE_TTI_INT_PIPE_NAME, ueLoop);
        rc = OS_SelectInit(&pUeInst->ttiIntSelector, ttiIntPipeName);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Init() OS_SelectInit(ttiIntSelector) error!\n");
        CSP_LOG_INF(UE_MD_L2_COMM, "UE_L2_Init() ue[%u] TTI_INT_PIPE[%s].\n", ueLoop, (char *)ttiIntPipeName);
    }

    rc = UE_L2_L1I_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Init() UE_L2_L1I_Init() error!\n");

    rc = UE_L2_L3I_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Init() UE_L2_L3I_Init() error!\n");

    CSP_LOG_INF(UE_MD_L2_COMM, "UE_L2_Init() success.\n");

    return O5G_RC_OK;
}

int32_t UE_L2_Final(void)
{
    int32_t rc;
    uint32_t ueLoop;
    S_UeL2UeInst *pUeInst;

    rc = OS_SelectFinal(&gpUeL2Context->ulTtiSelector);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Final() OS_SelectFinal(ulTtiSelector) error!\n");
    rc = OS_SelectFinal(&gpUeL2Context->dlTtiSelector);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Final() OS_SelectFinal(dlTtiSelector) error!\n");

    POLLER_Destroy(gpUeL2Context->ulPoller);
    POLLER_Destroy(gpUeL2Context->dlPoller);

    for (ueLoop = 0; ueLoop < UE_UE_INST_NUM_MAX; ueLoop++)
    {
        pUeInst = &gpUeL2Context->ueInst[ueLoop];
        IF_CONTINUE(==, FALSE, pUeInst->inUse, NO_LOG_MESSAGE);
        rc = OS_SelectFinal(&pUeInst->ttiIntSelector);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Final() OS_SelectFinal(ttiIntSelector) error!\n");
    }

    VarRB_Destroy(gpUeL2Context->pL3iL1iMsgQue);
    VarRB_Destroy(gpUeL2Context->pL1iL3iMsgQue);

    rc = UE_L2_L1I_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Final() UE_L2_L1I_Final() error!\n");

    rc = UE_L2_L3I_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Final() UE_L2_L3I_Final() error!\n");

    OS_MemFree(gpUeL2Context);

    CSP_LOG_INF(UE_MD_L2_COMM, "UE_L2_Final() success.\n");

    return O5G_RC_OK;
}

static void UE_L2_UL_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "UE_L2_UL_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, UE_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "UE_L2_UL_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);
}

static void UE_L2_UL_PtoProcess()
{

}

/* L2 UL部分：SDAP PDCP RLC MAC上行 */
static void *UE_L2_UL_Task(void* arg)
{
    int fd;
    int32_t rc;

    fd = gpUeL2Context->ulTtiSelector.recvFd;
    POLLER_Add(gpUeL2Context->ulPoller, fd, UE_L2_UL_TtiProcess, NULL, NULL, NULL);

    while (1)
    {
        rc = POLLER_Poll(gpUeL2Context->ulPoller, UE_L2_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            UE_L2_UL_PtoProcess();
        }
        pthread_testcancel();
    }
    return NULL;
}

static void UE_L2_DL_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "UE_L2_DL_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, UE_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "UE_L2_DL_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);
}

static void UE_L2_DL_PtoProcess()
{

}

/* L2 DL部分：SDAP PDCP RLC MAC下行 */
static void *UE_L2_DL_Task(void* arg)
{
    int fd;
    int32_t rc;

    fd = gpUeL2Context->dlTtiSelector.recvFd;
    POLLER_Add(gpUeL2Context->dlPoller, fd, UE_L2_DL_TtiProcess, NULL, NULL, NULL);

    while (1)
    {
        rc = POLLER_Poll(gpUeL2Context->dlPoller, UE_L2_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            UE_L2_DL_PtoProcess();
        }
        pthread_testcancel();
    }
    return NULL;
}

int32_t UE_L2_Start(void)
{
    int32_t rc;

    rc = UE_L2_L1I_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Start() UE_L2_L1I_Start() error!\n");

    rc = UE_L2_L3I_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Start() UE_L2_L3I_Start() error!\n");

    rc = OS_ThreadCreate(&gpUeL2Context->ulTid, UE_L2_UL_Task, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Start() OS_ThreadCreate(ulTid) error!\n");

    rc = OS_ThreadCreate(&gpUeL2Context->dlTid, UE_L2_DL_Task, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Start() OS_ThreadCreate(dlTid) error!\n");

    CSP_LOG_INF(UE_MD_L2_COMM, "UE_L2_Start() success.\n");

    return O5G_RC_OK;
}

int32_t UE_L2_Stop(void)
{
    int32_t rc;

    rc = UE_L2_L1I_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Stop() UE_L2_L1I_Stop() error!\n");

    rc = UE_L2_L3I_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Stop() UE_L2_L3I_Stop() error!\n");

    rc = OS_ThreadCancel(gpUeL2Context->ulTid);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Stop() OS_ThreadCancel(ulTid) error!\n");

    rc = OS_ThreadCancel(gpUeL2Context->dlTid);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L2_Stop() OS_ThreadCancel(dlTid) error!\n");

    CSP_LOG_INF(UE_MD_L2_COMM, "UE_L2_Stop() success.\n");

    return O5G_RC_OK;
}

